/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include <string>
#include <filesystem/String.hpp>
#ifdef IPHONE_VERSION
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#endif

#include <pwd.h>
#include <unistd.h>

namespace liba
{

namespace filesystem
{

static std::string anchor; // This dummy string does not allow multiple calls to StringStatics constructor

static std::string get_app_name()
{
	CFBundleRef main_bundle = CFBundleGetMainBundle();
	CFURLRef path_url = CFBundleCopyBundleURL( main_bundle );
	CFStringRef path_str = CFURLGetString( path_url );
	char buffer[1024];
	CFStringGetCString( path_str, buffer, 1024, CFStringGetSystemEncoding() );
//	CFRelease( path_str );
//	CFRelease( path_url );
    std::string path = buffer;
	
	if(path[path.size()-1] == '/')
	{
		path.resize(path.size()-1);
	}

	int pos = path.rfind( "/" );
	if( pos != std::string::npos )
		path = path.substr( pos+1, std::string::npos );

	path.resize(path.size()-4);	//strip .app symbols

	return path;
}

static std::string get_app_path()
{
	CFBundleRef main_bundle = CFBundleGetMainBundle();
	CFURLRef path_url = CFBundleCopyBundleURL( main_bundle );
	CFStringRef path_str_unescaped = CFURLGetString( path_url );
	CFStringRef path_str = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(0, path_str_unescaped, CFSTR(""), CFStringGetSystemEncoding());
	char buffer[1024];
	CFStringGetCString( path_str, buffer, 1024, CFStringGetSystemEncoding() );
//	CFRelease( path_str );
//	CFRelease( path_url );
    std::string path = buffer;
	int pos = path.find( "localhost" );
	if( pos != std::string::npos )
		path = path.substr( pos + 9, std::string::npos );
	
#ifndef IPHONE_VERSION	
	CFURLRef res_url = CFBundleCopyResourcesDirectoryURL( main_bundle );
	CFStringRef res_str = CFURLGetString( res_url );
	CFStringGetCString( res_str, buffer, 1024, CFStringGetSystemEncoding() );
//	CFRelease( res_str );
//	CFRelease( res_url );
	BasicString<char> res = buffer;
	
//	CFRelease( main_bundle );
	
	return path + "/" + res;
#endif
	
	return path;
}

static std::string get_system_path()
{
	return "";
}

static std::string get_windows_path()
{
	return "";
}

static std::string get_special_folder(int shell_folder_id)
{
	return "";
}

static std::string get_desktop_path()
{
	return "";
}
static std::string get_fonts_path()
{
	return "";
}
static std::string get_start_menu_path()
{
	return "";
}
static std::string get_start_menu_programs_path()
{
	return "";
}
static std::string get_common_desktop_path()
{
	return "";
}
static std::string get_common_start_menu_programs_path()
{
	return "";
}
static std::string get_common_start_menu_path()
{
	return "";
}

static std::string get_local_appdata()
{
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	CFStringRef res_dir = (CFStringRef)[paths objectAtIndex:0];

	char buffer[1024];
	CFStringGetCString( res_dir, buffer, 1024, CFStringGetSystemEncoding() );
    std::string res = buffer;
	res += "/";
	
	[pool release];
	
	return res;
}

StringStatics::StringStatics()
{
	//TODO: add system-dependent built-in templates
	//add %app_path% - dir to application
    std::string app_full_path = normalize( get_app_path() );
    std::string app_path = trim_file_name(app_full_path);

	templates["%app_path%"] = app_path;
	templates["%app_name%"] = normalize(get_app_name());
	templates["%system_path%"] = normalize( get_system_path() );
	templates["%windows_path%"] = normalize( get_windows_path() );
	templates["%desktop_path%"] = normalize( get_desktop_path() );
	templates["%fonts_path%"] = normalize( get_fonts_path() );
	templates["%start_menu_path%"] = normalize( get_start_menu_path() );
	templates["%start_menu_programs_path%"] = normalize( get_start_menu_programs_path() );
	templates["%common_desktop_path%"] = normalize( get_common_desktop_path() );
	templates["%common_start_menu_path%"] = normalize( get_common_start_menu_path() );
	templates["%common_start_menu_programs_path%"] = normalize( get_common_start_menu_programs_path() );
	templates["%local_appdata%"] = normalize( get_local_appdata() );
}

void StringStatics::add_path_instantiation(const std::string & from, const std::string & to)
{
	templates[from] = to;
}

std::string StringStatics::trim_extension(const std::string & str)
{
	size_t pos = str.rfind(".");
	const size_t npos = std::string::npos;
	if( pos == npos )
		return std::string();
	return str.substr(0, pos);
}

std::string StringStatics::trim_file_name(const std::string & str)
{
//	if( str.empty() )
//		return str;
	//if path ends with slash, treat it as the path to folder
//	if(str[str.size()-1] == '/')
//		return str;

	size_t pos = str.rfind("/");
	const size_t npos = std::string::npos;
	if( pos == npos )
		return std::string();
	return str.substr(0, pos + 1);
}

std::string StringStatics::normalize(const std::string & str)
{
    std::string result = str;
	//replace all backslashes with forward slashes
	std::string::iterator be = result.begin();
	for( ; be != result.end(); ++be )
	{
		if( *be == '\\' )
			*be = '/';
	}

	//lower all symbols
//	CharLower( result.lock() );

	//replace templates with instantiated values
	for(Templates::const_iterator i = templates.begin(); i != templates.end(); ++i)
	{
		if(result.substr(0, i->first.size()) == i->first)
		{
			result = i->second + result.substr(i->first.size());
			break;
		}
	}

	//replace sequences of slashes into single slashes
	if( result.size() > 1 )
	{
		size_t i = 1;
		while( i != result.size()-1 )
			if( result[i] == '/' && result[i+1] == '/' )
				result.erase( i+1, 1 );
			else
				++i;
	}

	return result;
}

void String::normalize()
{
	rep = statics().normalize( rep );
	
#ifdef IPHONE_VERSION
	//create tolowered copy for case sensitive file systems
	rep_for_comparison = rep;
	std::string::iterator be = rep_for_comparison.begin();
	be = rep_for_comparison.begin();
	for( ; be != rep_for_comparison.end(); ++be )
	{
		*be = tolower(*be);
	}
#endif	
}

void String::add_path_instantiation(const std::string & from, const std::string & to)
{
	statics().add_path_instantiation( from, to );
}

size_t String::find_next_slash(const String * s, size_t p1)
{
	const std::string & path = s->rep;
	size_t p2 = path.find("/", p1);
	
	//if next slash not found, return the rest of the path except for the slash at the end
	if(p2 == std::string::npos)
		p2 = path[path.size()-1] == '/' ? path.size() - 1 : path.size();

	return p2;
}

String::iterator String::begin()const
{
	//empty paths don't have elements
	if( rep.size() == 0 || rep == "/" )
		return end();

	size_t p1 = 0;
	size_t p2 = find_next_slash( this, p1 + 1 );
	return iterator( this, p1, p2 );
}

String::iterator & String::iterator::operator++()
{
	const std::string & path = parent->rep;
	const size_t npos = std::string::npos;

	//incrementing the last token pointer will result in p1 == p2 == npos
	if( p2 >= path.size() - 1 )
	{
		p1 = p2 = npos;
		return *this;
	}

	p1 = p2 + 1;
	p2 = find_next_slash( parent, p1 );

	return *this;
}

String String::trim_extension()const
{
	return statics().trim_extension( rep );
}

String String::trim_file_name()const
{
	return statics().trim_file_name( rep );
}

String String::get_file_name()const
{
	size_t pos = rep.rfind("/");
	const size_t npos = std::string::npos;
	if( pos == npos )
		return *this;
	if( pos == rep.size()-1 )
		return String();
	return rep.substr(pos + 1, npos);
}

String String::get_extension()const
{
	String fname = get_file_name();
	size_t pos = fname.rep.rfind(".");
	const size_t npos = std::string::npos;
	if( pos == npos )
		return *this;
	if( pos == fname.rep.size()-1 )
		return String();
	return fname.rep.substr(pos + 1, npos);
}

String combine(const String& left, const String& right)
{
	size_t leftSize = left.size();
	if (!leftSize)
		return right;
	
	if (left.c_str()[left.size() - 1] != '/')
		return (left + "/" + right);
	
	return left + right;
}

}//end of namespace filesystem

}//end of namespace liba
